#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <vector>

#include "Message.hpp"
#include "Logger.h"

const 	int		TERRAINPACKETSIZE	= 256;//Make your buffer larger than
const	int		MsgLengh	 		= 128;
char	buf[ TERRAINPACKETSIZE ];



using boost::asio::ip::udp;

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 7 )
    {
      std::cerr << "Usage: client <host> <port> <own port> <SensorName> <DataType? 0: Temperature, 1: traffic > <interactive> 0 no :1 yes>" << std::endl;
      return 1;
    }


		boost::asio::io_service io_service;
		DataType type	= (DataType)atoi( argv[ 5 ] );
		udp::endpoint receiver_endpoint( boost::asio::ip::address::from_string( argv[ 1 ]) ,
									 atoi( argv[ 2 ] ));

		udp::socket socket(io_service);
		socket.open(udp::v4());

		udp::endpoint local_endpoint( boost::asio::ip::address::from_string( "127.0.0.1" ), atoi(argv[3]) );
		socket.bind( local_endpoint );

		int id			 = 0;
		bool EntityAcked = false;
		std::string			strBuf;
		Message				ReceivedMessage;

		bool Interactive = atoi(argv[ 6 ]) == 1 ? true : false ;
		Logger LOGGER( std::cout , true, false  );

		MsgPointer msgEntity( new Message() );
		msgEntity->ID 		= "0";
		msgEntity->m_type	= REQ;
		msgEntity->m_DataType	= CSS;
		msgEntity->m_action		= ENTITY;
		msgEntity->data			= "Sensor";
		msgEntity->m_strSenderID= argv[ 4 ];

		std::string strEntity =  msgEntity->Encode();


		MsgPointer SubmitData( new Message() );
		SubmitData->ID = IntToString( id++ );
		SubmitData->m_type		= REQ;
		SubmitData->m_DataType		= type;
		SubmitData->m_action		= SUBMIT;
		SubmitData->data			= IntToString( rand()&80 );
		SubmitData->m_strSenderID	= argv[ 4 ];

		std::string SubmitStr = SubmitData->Encode();

		socket.send_to( boost::asio::buffer( strEntity.c_str(), strEntity.length() ), receiver_endpoint);
		udp::endpoint sender_endpoint;

		while( !EntityAcked )
		{
			socket.send_to( boost::asio::buffer( strEntity.c_str(), strEntity.length() ), receiver_endpoint);
			size_t len = socket.receive_from( boost::asio::buffer( buf, 128 ), sender_endpoint );
			std::cout<<"Sensor received "<<len<<"bytes "<< buf<<"\n";
			strBuf = buf;
			ReceivedMessage.Decode( strBuf );
			if( ACK == ReceivedMessage.m_type )
			{
				if( ENTITY == ReceivedMessage.m_action )
				{
					std::cout<<"Sensor Acknowledged \n";
					EntityAcked = true;
				}
			}
		}

		if( Interactive )
		{
			LOGGER<<"Please Enter Data to send \n";
			std::string userData;
			std::cin>>userData;
			SubmitData->data = userData;

		}
		else
		{
			SubmitData->data	= IntToString( rand()&80 );
		}
		SubmitStr = SubmitData->Encode();
		socket.send_to( boost::asio::buffer( SubmitStr.c_str(), SubmitStr.length() ), receiver_endpoint);
		while( true )
		{
			size_t len = socket.receive_from( boost::asio::buffer( buf, 128 ), sender_endpoint );
			LOGGER<<"Sensor received "<<len<<"bytes "<< buf<<"\n";
			strBuf = buf ;
			ReceivedMessage.Decode( strBuf );
			if( ReceivedMessage.m_action == SUBMIT )
			{
					//Send New Submit message.
					SubmitData->ID		= IntToString( id++ );

					if( Interactive )
					{
						LOGGER<<"Please Enter Data to send \n";
						std::string userData;
						std::cin>>userData;
						SubmitData->data = userData;

					}
					else
					{
						SubmitData->data	= IntToString( rand()&80 );
					}

					std::cout<<"Sensor Submit Data request: "<< SubmitStr <<"\n";
					SubmitStr = SubmitData->Encode();
					socket.send_to( boost::asio::buffer( SubmitStr.c_str(), SubmitStr.length() ), receiver_endpoint);
			}
			sleep( rand()%5 );
		}


	}
	catch (std::exception& e)
	{
		std::cerr << e.what() << std::endl;
	}
 return 0;
}


//
//udp::endpoint local_endpoint( boost::asio::ip::address::from_string( "127.0.0.1" ), atoi(argv[3]) );
//socket.bind( local_endpoint );
//
//clientName = argv[ 4 ];
//
//MsgPointer msg( new Message() );
//msg->ID 		= IntToString( id++ );
//msg->m_MsgType	= REQ;
//msg->m_DataType	= CSS;
//msg->m_action	= ENTITY;
//msg->data		= "Client";
//msg->m_strSenderID = clientName;
//
//std::string strEntityMsg =  msg->Encode();
//
//MsgPointer pullmsg( new Message() );
//pullmsg->ID 		= IntToString( id++ );
//pullmsg->m_MsgType	= REQ;
//pullmsg->m_DataType	= Temp;
//pullmsg->m_action	= PULL;
//pullmsg->data		= "GiveMeData";
//pullmsg->m_strSenderID	= clientName;
//G_pullmsg				= pullmsg;
//PullStr = pullmsg->Encode();
//
//while( !EntityAcked )
//{
//	socket.send_to( boost::asio::buffer( strEntityMsg.c_str(), strEntityMsg.length() ), receiver_endpoint);
//	size_t len = socket.receive_from( boost::asio::buffer( buf, 128 ), sender_endpoint );
//	std::cout<<"Client received "<<len<<"bytes "<< buf<<"\n";
//	strBuf = buf;
//	ReceivedMessage.Decode( strBuf );
//	if( ACK == ReceivedMessage.m_MsgType )
//	{
//		if( ENTITY == ReceivedMessage.m_action )
//		{
//			std::cout<<"Client Acknowledged \n";
//			EntityAcked = true;
//		}
//	}
//}
//
////Send the first pull message.
//time( &pullmsg->m_SendTime );
//socket.send_to( boost::asio::buffer( PullStr.c_str(), PullStr.length() ), receiver_endpoint);
//
//while( true )
//{
//	size_t len = socket.receive_from( boost::asio::buffer( buf, 128 ), sender_endpoint );
//	std::cout<<"Client received "<<len<<"bytes "<< buf<<"\n";
//	strBuf = buf;
//	ReceivedMessage.Decode( strBuf );
//	if( ReceivedMessage.m_action == SUBMIT )
//	{
//		time( &pullmsg->m_ackTime );
//		LOGGER<<"Time between Pull message with ID"<<pullmsg->ID<<" and its response(approximate) = "<<( difftime( pullmsg->m_ackTime , pullmsg->m_SendTime ) )<<" seconds \n";
//	}
//	if( ReceivedMessage.m_action == PUSH  )
//	{
//		//Ack Message.
//		SendAck( ReceivedMessage );
//	}
//	if( pullmsg->IsTimedOut( TimeOutPeriod ) )
//	{
//		LOGGER<<"Pull Message"<<pullmsg->ID<<"Time out, resending\n";
//		socket.send_to( boost::asio::buffer( PullStr.c_str(), PullStr.length() ), receiver_endpoint);
//		pullmsg->UpdateResend();
//		//Don't send new message
//		continue;
//	}
//
//	sleep( rand()%5 );
//
//	time( &pullmsg->m_SendTime );
//	pullmsg->ID = IntToString( id++ );
//	PullStr = pullmsg->Encode();
//	pullmsg->ResetResend();
//	std::cout<<"Sending pull request: "<< PullStr <<"\n";
//	socket.send_to( boost::asio::buffer( PullStr.c_str(), PullStr.length() ), receiver_endpoint);
//}
//
